#include <stdlib.h>
#include <string.h>
#include "clib_tcp_buffer.h"
#include "clib_stack_trace.h"

#define STACK_MTU                     1460

#define BUFFER_MIN_LENGTH(a,b)      (a) > (b) ? (b) : (a)

#define CLIB_BUFF_SEQ_AFTER(a,b)    (i32_t)((a)-(b))

i32_t 
clib_tcp_buffer_init(tcp_buffer_t *buffer, u16_t size, u16_t node_n)
{
    struct tcp_frag_node *node;
    i32_t            offset;

    memset(buffer, 0, sizeof(tcp_buffer_t));
    buffer->node   = node_n;
    buffer->size   = size;

    clib_queue_init(&buffer->used);
    clib_queue_init(&buffer->free);

    offset = sizeof(struct tcp_frag_node) * node_n;
    if(size < offset) {
        return rn_error;
    }

    for(int i = 0; i < node_n; i++) {
        node = &((struct tcp_frag_node*)buffer->data)[i];
        clib_queue_init(&node->frag_list);
        clib_queue_insert_tail(&buffer->free, &node->frag_list);
    }
   
    buffer->value = &buffer->data[offset];

    buffer->value_len = size - offset;

    return rn_ok;
}

i32_t 
clib_tcp_buffer_set_seq(tcp_buffer_t *buffer, u32_t seq)
{
    buffer->seq = seq;
    buffer->init = 1;
    return rn_ok;
}

i32_t 
clib_tcp_buffer_recv_put_data(tcp_buffer_t *buffer, u32_t seq, u08_t* data, u16_t data_len)
{
    u16_t                 copy_len;
    u16_t                 offset;
    struct tcp_frag_node *node;
    struct tcp_frag_node *frist;
    struct tcp_frag_node *new;
    clib_queue_t         *prev;
    clib_queue_t         *next;

    /** TODO 考虑数据回绕 **/
    /** TODO 增加 node_offset 判断 **/
    if( clib_queue_empty(&buffer->free) || 
        ((seq + data_len) - buffer->seq) > buffer->value_len) {
        return rn_error;
    }

    frist= clib_queue_next(&buffer->used);
    new  = clib_queue_next(&buffer->free);
    prev = clib_queue_prev(&buffer->used);

    /** TODO 删除 **/
    if(clib_queue_empty(&buffer->used)) {

        if(!buffer->seq) {
            buffer->seq = seq;
            buffer->init = 1;
        }
        
        /** TODO  数据回绕 **/ 
        new->offset = (seq - buffer->seq + buffer->offset) % buffer->value_len;
        new->seq    = seq;
        new->length = data_len;
        if(new->offset + data_len <= buffer->value_len) {
            memcpy(&buffer->value[new->offset], data, data_len);
        } else {
            copy_len = buffer->value_len - (new->offset);
            memcpy(&buffer->value[new->offset], data, copy_len);
            memcpy(buffer->value, &data[copy_len], data_len - copy_len);
        }
        clib_queue_del(&new->frag_list);
        clib_queue_insert_after(&buffer->used, &new->frag_list);
        if(buffer->seq == new->seq){
            buffer->ready_len += data_len;
        } else {
            buffer->frag_len  += data_len;
        }
        
        return rn_ok;
    }
    
    for(; prev != clib_queue_sentinel(&buffer->used); prev = clib_queue_prev(prev))
    {
        node = (struct tcp_frag_node*)prev;
        if(node->seq + node->length == seq) {
            new->seq    = seq;
            new->length = data_len;
            new->seq = seq;
            new->offset = (node->offset + node->length) % buffer->value_len;
            if(new->offset + data_len <= buffer->value_len) {
                memcpy(&buffer->value[new->offset], data, data_len);
            } else {
                copy_len = buffer->value_len - (new->offset);
                memcpy(&buffer->value[new->offset], data, copy_len);
                memcpy(buffer->value, &data[copy_len], data_len - copy_len);
            }
            clib_queue_del(&new->frag_list);
            clib_queue_insert_after(prev, &new->frag_list);
            /** TODO 逻辑验证 **/
            if(seq - buffer->seq == buffer->ready_len) {
                buffer->ready_len += data_len;
                next = clib_queue_next(&new->frag_list);
                for(; next != clib_queue_sentinel(&buffer->used); next = clib_queue_next(next)) {
                    /** 合并连续长度 **/
                    node = (struct tcp_frag_node*)next;
                    if(new->seq + new->length == node->seq) {
                        buffer->ready_len += node->length;
                        buffer->frag_len  -= node->length;
                        new = node;
                    } else {
                        break;
                    }
                }
            }
            break;
        } else if(CLIB_BUFF_SEQ_AFTER(node->seq + node->length, seq) > 0/*node->seq + node->length > seq*/) {

            if(clib_queue_sentinel(&buffer->used) == clib_queue_prev(prev)) {

                /** TODO 数据回绕     **/
                /** TODO 计算 偏移位置**/
                new->length = data_len;
                new->seq = seq;
                new->offset = (seq - buffer->seq + buffer->offset) % buffer->value_len;
                if(new->offset + data_len <= buffer->value_len) {
                    memcpy(&buffer->value[new->offset], data, data_len);
                } else {
                    copy_len = buffer->value_len - (new->offset);
                    memcpy(&buffer->value[new->offset], data, copy_len);
                    memcpy(buffer->value, &data[copy_len], data_len - copy_len);
                }
                
                clib_queue_del(&new->frag_list);
                clib_queue_insert_head(&buffer->used,&new->frag_list);
                
                if(seq == buffer->seq) {
                    buffer->ready_len += data_len;
                    next = clib_queue_next(&new->frag_list);
                    for(; next != clib_queue_sentinel(&buffer->used); next = clib_queue_next(next)) {
                        node = (struct tcp_frag_node*)next;
                        if(new->seq + new->length == node->seq) {
                            buffer->ready_len += node->length;
                            buffer->frag_len  -= node->length;
                            new = node;
                        } else {
                            break;
                        }
                    }
                } else {
                     buffer->frag_len += data_len;
                }
                break;
            } else {
                continue;
            }
            
        } else if(node->seq == seq) {
            break;
        } else if(CLIB_BUFF_SEQ_AFTER(node->seq + node->length, seq) < 0 /* node->seq + node->length < seq */) {
            /** TODO 数据回绕     **/
            /** TODO 计算 偏移位置**/
            new->length = data_len;
            new->seq = seq;
            new->offset = (seq - buffer->seq + buffer->offset) % buffer->value_len;
            if(new->offset + data_len <= buffer->value_len) {
                memcpy(&buffer->value[new->offset], data, data_len);
            } else {
                copy_len = buffer->value_len - (new->offset);
                memcpy(&buffer->value[new->offset], data, copy_len);
                memcpy(buffer->value, &data[copy_len], data_len - copy_len);
            }
            clib_queue_del(&new->frag_list);
            clib_queue_insert_after(prev, &new->frag_list);
            buffer->frag_len += data_len;
            break;
        }
    }

    return rn_ok;
}

i32_t 
clib_tcp_buffer_recv_get_data(tcp_buffer_t *buffer, u08_t* data, u16_t data_len) 
{
    struct tcp_frag_node   *node;
    struct tcp_frag_node   *frist;
    clib_queue_t           *next;
    clib_queue_t           *prev;
    u16_t                   offset;
    u16_t                   copy_len;
    u16_t                   temp_len;

    copy_len = BUFFER_MIN_LENGTH(buffer->ready_len, data_len);

    buffer->ready_len -= copy_len;
    next = clib_queue_next(&buffer->used);
    frist = (struct tcp_frag_node*)next;
    offset = (frist->offset + buffer->node_offset) % buffer->value_len;

    if(offset + copy_len <= buffer->value_len) {
        memcpy(data, &buffer->value[offset], copy_len);
    } else {
        temp_len = buffer->value_len - (offset);
        memcpy(data, &buffer->value[offset], temp_len);
        memcpy(&data[temp_len], buffer->value, copy_len - temp_len);
    }

    temp_len = copy_len;
    for( ; next != clib_queue_sentinel(&buffer->used);) {
        prev = next;
        node = (struct tcp_frag_node*)next;
        if(temp_len >= (node->length - buffer->node_offset)) {
            if(buffer->node_offset == 0) {
                temp_len -= node->length;
            } else {
                temp_len -= (node->length - buffer->node_offset);
                buffer->node_offset = 0;
            }

            // if(temp_len == 0) {
            //     buffer->seq =  node->seq + node->length;
            //     buffer->offset  = (node->offset + node->length) % buffer->value_len;
            //     buffer->node_offset = 0;
            //     clib_queue_del(next);
            //     clib_queue_add(&buffer->free, next);
            // }

        } else {
            buffer->seq = node->seq;
            buffer->offset  = node->offset;
            buffer->node_offset += temp_len;
            break;
        }

        if(temp_len >= 0) {
            buffer->seq =  node->seq + node->length;
            buffer->offset  = (node->offset + node->length) % buffer->value_len;
            buffer->node_offset = 0;
        }

        next = clib_queue_next(next);
        clib_queue_del(prev);
        clib_queue_add(&buffer->free, prev);
    }

    return copy_len;
}

i32_t 
clib_tcp_buffer_send_put_data(tcp_buffer_t *buffer, u08_t* data, u16_t data_len)
{
    struct tcp_frag_node *last;
    u16_t                 copy_len;
    u16_t                 temp_len;
    u16_t                 offset;
    
    if(clib_queue_empty(&buffer->used)) {
        copy_len = buffer->value_len - buffer->ready_len;
        copy_len = BUFFER_MIN_LENGTH(copy_len, data_len);
        offset = (buffer->offset + buffer->ready_len) % buffer->value_len;
    } else {
        last = (struct tcp_frag_node*)clib_queue_prev(&buffer->used);
        offset = (buffer->ready_len + last->offset + last->length) % buffer->value_len;
        copy_len = buffer->value_len - (last->seq - buffer->seq + last->length + buffer->ready_len);
        copy_len = BUFFER_MIN_LENGTH(copy_len, data_len);
    }
    
    if(!copy_len) {
        return 0;
    }
    
    if(offset + copy_len < buffer->value_len) {
        memcpy(&buffer->value[offset], data, copy_len);
    } else {
        temp_len = buffer->value_len - (offset);
        memcpy(&buffer->value[offset], data, copy_len);
        memcpy(&buffer->value, &data[temp_len], copy_len - temp_len);
    }

    buffer->ready_len += copy_len;
    
    return (i32_t)copy_len;
}

i32_t 
clib_tcp_buffer_send_get_data(tcp_buffer_t *buffer, u32_t seq, u08_t* data, u16_t *data_len)
{
    u16_t  offset;
    u16_t  copy_len;
    u16_t  temp_len;
    struct tcp_frag_node *node;
    struct tcp_frag_node *last;

    if(clib_queue_empty(&buffer->free) || !buffer->ready_len) {
        *data_len = 0;
        return 0;
    }
    
    copy_len = BUFFER_MIN_LENGTH(buffer->ready_len, *data_len);

    copy_len = BUFFER_MIN_LENGTH(STACK_MTU, copy_len);

    node = (struct tcp_frag_node*)clib_queue_next(&buffer->free);

    if(clib_queue_empty(&buffer->used)) {

        offset = buffer->offset;

        if(offset + copy_len <= buffer->value_len) {
            memcpy(data, &buffer->value[offset], copy_len);
        } else {
            temp_len = buffer->value - (offset + 1);
            memcpy(data, &buffer->value[offset], temp_len);
            memcpy(&data[offset], &buffer->value, copy_len - temp_len);
        }
        
        node->seq = seq;
        node->length = copy_len;
        node->offset = offset;
        clib_queue_del(&node->frag_list);
        clib_queue_insert_tail(&buffer->used, &node->frag_list);
    } else {
        last = (struct tcp_frag_node*)clib_queue_prev(&buffer->used);

        offset = (last->offset + last->length) % buffer->value_len;

        if(offset + copy_len < buffer->value_len) {
            memcpy(data, &buffer->value[offset], copy_len);
        } else {
            temp_len = buffer->value - (offset + 1);
            memcpy(data, &buffer->value[offset], temp_len);
            memcpy(&data[offset], &buffer->value, copy_len - temp_len);
        }

        node->seq = seq;
        node->length = copy_len;
        node->offset = offset;

        clib_queue_del(&node->frag_list);

        clib_queue_insert_tail(&buffer->used, &node->frag_list);
    }
    /** 未确认的数据不能去除 **/
    //buffer->ready_len -= copy_len;
    *data_len = copy_len;
    
    return (i32_t)copy_len;
}

i32_t 
clib_tcp_buffer_send_ack_data(tcp_buffer_t *buffer, u32_t ack)
{
    struct tcp_frag_node   *node;
    clib_queue_t           *next;
    clib_queue_t           *prev;
    i32_t                   is_ok = false;
        
    if(clib_queue_empty(&buffer->used)) {
        return rn_error;
    }

    /** TODO 判断ACK合法性 **/
    node = (struct tcp_frag_node*)clib_queue_prev(&buffer->used);
    if(node->length + node->seq < ack) {
        return rn_error;
    }

    next = clib_queue_next(&buffer->used);
    
    for( ;next != clib_queue_sentinel(&buffer->used); ) {
        prev = next;
        node = (struct tcp_frag_node*)next;
        if(CLIB_BUFF_SEQ_AFTER(node->seq + node->length, ack) < 0 /*node->seq + node->length < ack */) {
            buffer->ready_len -= node->length;
            /** TODO offset 位置偏移计算 **/
            buffer->offset = (node->offset + node->length) % buffer->value_len;
            /** to do **/
        } else if(node->seq + node->length == ack) {
            buffer->ack = ack;
            buffer->seq = ack;
            buffer->offset = (node->offset + node->length) % buffer->value_len;
            buffer->ready_len -= node->length;
            is_ok = true;
            clib_queue_del(prev);
            clib_queue_add(&buffer->free, prev);
            break;
        } else if(node->seq == ack) {
            clib_stack_trace("tcp ack seq out-of-order node->seq == ack\n");
            break;
        } else {
            clib_stack_trace("tcp ack seq out-of-order node->seq > ack\n");
            break;
        }

        next = clib_queue_next(next);
        clib_queue_del(prev);
        clib_queue_add(&buffer->free, prev);
    }

    if(!is_ok) {
        clib_stack_trace("tcp ack seq out-of-order\n");
        return rn_error;
    }

    return rn_ok;
}